package ece.edt;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.httpclient.HttpException;
import org.apache.log4j.Logger;

import ece.edt.HyperPlanningMethod.Function;
import ece.edt.exceptions.HyperPlanningException;
import ece.edt.exceptions.HyperPlanningMethodException;
import ece.edt.exceptions.ScheduleException;

public class HyperPlanning {
	private static final Logger log = Logger.getLogger(HyperPlanning.class);
	private final String fServiceUrl;
	protected CASClient casClient;
	private static int session;
	private static int numeroOrdreCommunication;
	private String weeksValidated;
	private int startWeek;
	
	private static HyperPlanning hyperPlanning;
	
	public static class User{
		private String name;
		private String id;
		private Ressource type;
		
		User(String n, String i, Ressource t){
			this.name = n;
			this.id = i;
			this.type = t;
		}
		
		public User(String n, String i, String t) {
			this.name = n;
			this.id = i;
			this.type = Ressource.valueOf(t);
		}

		public String getName(){
			return name;
		}
		
		public String getID(){
			return id;
		}
		
		public Ressource getType(){
			return type;
		}
	}
	
	protected enum Ressource{
		PROFESSEUR("0"),
		GROUPE("1"),
		CLASSE("2"),
		TD("3"),
		OPTION("4"),
		SALLE("5");
		
		private String number;
		
		Ressource(String n){
			this.number = n;
		}
		
		public String getID(){
			return number;
		}
	}
	
	public static HyperPlanning getInstance(CASClient c) throws HyperPlanningException{
		if(hyperPlanning == null){
			hyperPlanning = new HyperPlanning(c);
		}
		
		return hyperPlanning;
	}
	
	protected HyperPlanning(CASClient c) throws HyperPlanningException{
		casClient = c;
		fServiceUrl = casClient.getServiceUrl();
		initConnection();
	}
	
	protected void initConnection() throws HyperPlanningException{
		HyperPlanningMethod method = null;

		session = 0;
		numeroOrdreCommunication = 0;
		
		try {
			method = new HyperPlanningMethod(Function.Parametres, fServiceUrl);
			casClient.executeMethod(method);
			
			String xml = method.getXML();
			session = method.getSession();
			numeroOrdreCommunication = method.getNumeroOrdreCommunication();
			
			Pattern pattern = Pattern.compile("<PremiereDate T=\"7\"><!\\[CDATA\\[([0-9]{2})/([0-9]{2})/([0-9]{4})\\]\\]></PremiereDate>");
			Matcher matcher = pattern.matcher(xml);
			if (matcher.find()) {
				Calendar c = Calendar.getInstance();
				c.set(Integer.parseInt(matcher.group(3)), Integer.parseInt(matcher.group(2))-1, Integer.parseInt(matcher.group(1)));
				
				startWeek = c.get(Calendar.WEEK_OF_YEAR);
			}
			
			pattern = Pattern.compile("ConserverNumerosCalendaires><PeriodeConsultation T=\"8\"><!\\[CDATA\\[([0-1]{52})\\]\\]></PeriodeConsultation>");
			matcher = pattern.matcher(xml);
			
			if (matcher.find()) {
				weeksValidated = matcher.group(1);
			}
			
			if(session == 0 && numeroOrdreCommunication == 0) {
				throw new HyperPlanningException("Getting session number");
			}
		} catch (HyperPlanningMethodException e) {
			throw new HyperPlanningException(e);
		} catch (HttpException e) {
			log.error(e);
		} catch (IOException e) {
			log.error(e);
		} finally {
			method.releaseConnection();
		}
	}
	
	public User getUser(String name) throws HyperPlanningException{
		List<User> users;
		User user = null;
		
		users = getUsers(name, Ressource.CLASSE);
		for (User u : users) {
			if (name.equalsIgnoreCase(u.getName())) {
				user = u;
				break;
			}
		}
		
		if (user == null) {
			users = getUsers(name, Ressource.PROFESSEUR);
			for (User u : users) {
				if (name.equalsIgnoreCase(u.getName())) {
					user = u;
					break;
				}
			}
		}
		
		if (user == null) {
			throw new HyperPlanningException("Couldn't find user : " + name);
		}

		return user;
	}	
	
	public List<User> getUsers(String beginWith, Ressource type){
		List<User> users = new ArrayList<User>();
		
		String[][] values = {
				{"NOM_RESSOURCE", beginWith.split(" ")[0]},
				{"GENRE_RESSOURCE", type.getID()}};
		try {
			HyperPlanningMethod method = new HyperPlanningMethod(Function.RenvoyerListeDeRessource, values, fServiceUrl);
			
			try {
				casClient.executeMethod(method);

				Pattern pattern = Pattern
						.compile("<NOM T=\"3\"><!\\[CDATA\\[(.+)\\]\\]></NOM><NUMERO T=\"3\"><!\\[CDATA\\[(.+)\\]\\]></NUMERO>");
				Matcher matcher = pattern.matcher(method.getXML());
				if (matcher.find()) {
					String[] names = matcher.group(1).split("!!");
					String[] ids = matcher.group(2).split("!!");

					int length = names.length;

					for (int i = 0; i < length; i++) {
						users.add(new User(names[i], ids[i], type));
					}
				}
			} finally {
				method.releaseConnection();
			}
		} catch (IOException e) {
			log.error(e, e);
		} catch (HyperPlanningMethodException e) {
			log.error(e, e);
		}
		
		return users;
	}
	
	public List<User> getUsers(String beginWith){
		List<User> users = getUsers(beginWith, Ressource.CLASSE);
		users.addAll(getUsers(beginWith, Ressource.PROFESSEUR));
		
		return users;
	}
	
	private String getWeekXML(User user, int week) throws HyperPlanningException{
		HyperPlanningMethod method = null;
		String xml = null;

		String[][] values = {
			{"GENRE_RESSOURCE", user.getType().getID()},
			{"NUMERO_RESSOURCE", user.getID()},
			{"NUMERO_SEMAINE", Integer.toString(week)}};
		
		
		method = new HyperPlanningMethod(Function.EmploiDuTemps, values, fServiceUrl);
		try {
			casClient.executeMethod(method);
			xml = method.getXML();
		} catch (IOException e) {
			log.error(e);
		} finally {
			method.releaseConnection();
		}

		return xml;
	}
	
	private String getWeeksAvailable(User user, boolean getWeeksNonValidated) throws HyperPlanningException{
		String weeks = weeksValidated;
		
		if (getWeeksNonValidated) {
			String[][] values = {
					{ "GENRE_RESSOURCE", user.getType().getID() },
					{ "NUMERO_RESSOURCE", user.getID()}};
			
			HyperPlanningMethod method = new HyperPlanningMethod(Function.DomaineDePresence, values, fServiceUrl);
			
			try {
				casClient.executeMethod(method);

				Pattern pattern = Pattern
						.compile("<DOMAINEPRESENCE T=\"8\"><!\\[CDATA\\[([0-1]{52})\\]\\]></DOMAINEPRESENCE>");
				Matcher matcher = pattern.matcher(method.getXML());
				if (matcher.find()) {
					weeks = matcher.group(1);
				}
			} catch (IOException e) {
				log.error(e);
			} finally {
				method.releaseConnection();
			}
		}
		
		return weeks;
	}

	public static int getSession() {
		return session;
	}

	public static int getNumeroordrecommunication() {
		return numeroOrdreCommunication;
	}
	
	public Schedule grab(User user, boolean getWeeksNonValidated) throws HyperPlanningException{
		Schedule schedule = null;
		
		try {
			String weeks = getWeeksAvailable(user, getWeeksNonValidated);
			schedule = new Schedule(user.getName().split(" ")[0]);
				
			int actualWeek = Calendar.getInstance().get(Calendar.WEEK_OF_YEAR);
			for(int i = (actualWeek - startWeek + 52)%52; i < 52; i++){
				if(weeks.charAt(i) == '1'){
					schedule.addWeek(getWeekXML(user, i+1));
				}
			}
		
		}catch(ScheduleException e) {
			throw new HyperPlanningException(e);
		}
		
		return schedule;
	}
}
